#pragma once

#include "protocol.hpp"
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <unistd.h>
#include <functional>

#define gbacklog 5

using namespace std;

enum
{
    USAGE_ERR = 1,
    SOCK_ERR,
    BIND_ERR,
    LISTEN_ERR,
    READ_ERR
};

typedef function<bool(const httpRequest &, httpResponse &)> func_t;

void handlerEnter(int sock, func_t callback)
{
    // 1. 读取。
    // 1.1 如何保证读取到的消息是 【一个】【完整的】请求
    // 2. 对读取到的请求Request反序列化
    // 3. 计算机处理，业务逻辑。
    // 4. 对响应Response序列化
    // 5. 发送

    // 这里没有实现保证读到一个完整的报文
    httpRequest req;
    char buf[4096];
    while (1)
    {
        int n = recv(sock, buf, sizeof(buf) - 1, 0);
        if (n > 0)
        {
            buf[n] = 0;
            req.inbuf = buf;
            req.parse();

            httpResponse resp;
            if (!callback(req, resp))
                return;

            send(sock, resp.outbuf.c_str(), resp.outbuf.size(), 0);
        }
    }
}

class Server
{
public:
    Server(const uint16_t &port, func_t func)
        : _listenSock(-1), _port(port), _func(func)
    {
    }

    void initServer()
    {
        // 1.创建套接字
        _listenSock = socket(AF_INET, SOCK_STREAM, 0);
        if (_listenSock < 0)
        {
            exit(SOCK_ERR);
        }

        // 2.绑定端口号
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY);
        int n = bind(_listenSock, (struct sockaddr *)&local, sizeof(local));
        if (n == -1)
        {
            exit(BIND_ERR);
        }

        // 3.设置监听状态
        n = listen(_listenSock, gbacklog);
        if (n < 0)
        {
            exit(LISTEN_ERR);
        }
    }

    void start()
    {
        struct sockaddr_in client;
        socklen_t len = sizeof(client);
        while (1)
        {
            signal(SIGCHLD, SIG_IGN); // version 2

            // 4.接收连接请求，建立连接
            int sock = accept(_listenSock, (struct sockaddr *)&client, &len);
            if (sock < 0)
            {
                continue;
            }

            /********************version2 多进程，子进程会拷贝父进程的进程地址空间*****************/
            pid_t id = fork();
            if (id == 0) // 子进程
            {
                close(_listenSock);

                handlerEnter(sock, _func);
                close(sock);
                exit(0);
            }

            close(sock);
        }
    }

    ~Server()
    {
    }

private:
    int _listenSock;
    uint16_t _port;
    func_t _func;
};
